#define quickread
#ifdef quickread
#include <cstdio> 
template <typename T>
inline void read(T& x)
{
    int c=getchar(), f=1; x=0;
    while(c<'0'||'9'<c) {if(c=='-') f=-1; c=getchar();}
    while('0'<=c&&c<='9') 
        x=(x<<3)+(x<<1)+c-'0', c=getchar();
    x*=f;
}
template <typename T>
inline void quickwrite(T x)
{
    if(x<0) putchar('-'), x*=-1;
    if(x>=10) quickwrite(x/10);
    putchar(x%10+'0');
}
#else 
#include <iostream>
#endif
#include <algorithm>
#include <cstring>
using namespace std;
#define DEBUG
const int N=1000010, M=N<<1;
int n, m;
struct Data
{
    int a, b, c, cnt, res; //每个元素出现多少次 有多少个元素是匹配的
    bool operator<(const Data& t) const
    {
        if(a!=t.a) return a<t.a;
        if(b!=t.b) return b<t.b;
        return c<t.c;
    }
    bool operator==(const Data& t) const
    {
        return a==t.a && b==t.b && c==t.c;
    }
}q[N], w[N];

int tree[N]; //树状数组维护左半部分的c,统计右半部分中满足c'<=c的个数
//tree[i]:区间[i-lowbit(i)+1, i]内元素出现次数总和
int ans[N]; //ans[i]=d 排名为i的元素个数

inline int lowbit(int x)
{
    return x&-x;
}

void add(int x, int v) //c==x的元素出现次数v插入到树状数组中
{
    for(int i=x; i<M; i+=lowbit(i)) tree[i]+=v;
}

int query(int x) //查询树状数组中,第三维<=x的元素总个数
{
    int res=0;
    for(int i=x; i; i-=lowbit(i)) res+=tree[i];
    return res;
}

void merge_sort(int l, int r)
{
    if(l>=r) return;
    int mid=l+r>>1;
    merge_sort(l, mid), merge_sort(mid+1, r); //实现第二维b的分治
    int i=l, j=mid+1, k=0;
    while(i<=mid&&j<=r)
        if(q[i].b<=q[j].b) add(q[i].c, q[i].cnt), w[k++]=q[i++]; //优先处理左半部分较小的b，在树状数组中插入c值
        else q[j].res+=query(q[j].c), w[k++]=q[j++]; //右半元素的b较小，此时左半中小于当前b的一插入完毕，查询左半中c'<=当前c的个数
        while(i<=mid) add(q[i].c, q[i].cnt), w[k++]=q[i++];
        while(j<=r) q[j].res+=query(q[j].c), w[k++]=q[j++];
    for(int i=l; i<=mid; i++) add(q[i].c, -q[i].cnt);
    for(int i=l, j=0; j<k; i++, j++) q[i]=w[j]; //恢复现场
}

void init()
{
    read(n), read(m);
    for(int i=0; i<n; i++)
    {
        int a, b, c; read(a), read(b), read(c);
        q[i]={a, b, c, 1};
    }
    sort(q, q+n); //实现第一维a的分治
    
}

void solve()
{
    init();
    int k=1;
    for(int i=1; i<n; i++) //去重
        if(q[i]==q[k-1]) q[k-1].cnt++; 
        else q[k++]=q[i];

    merge_sort(0, k-1);
    for(int i=0; i<k; i++)
        ans[q[i].res+q[i].cnt-1] += q[i].cnt; //f[i]=d d:当前有多少是匹配的+除自身的相同val的个数

    for(int i=0; i<n; i++)
        quickwrite(ans[i]), puts("");
}

// #undef DEBUG
signed main()
{
    #ifdef DEBUG
        freopen("./in.txt", "r", stdin);
        freopen("./out.txt", "w", stdout);
    #endif
    #ifndef quickread
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    #endif

    int T=1; //scanf("%d", &T);
    while(T--) 
    {
        solve();
    }
    return 0;
}
